import EventManager from "../core/EventManager";
import PopupManager from "../core/PopupManager";
import ResManager from "../core/ResManager";
import { CommonEvents } from "../net/CommonEvents";
import Singleton from "../net/Singleton";
import LogUtil from "../utils/LogUtil";

var MD5 = require('MD5');

export default class BundleUpdateModule extends Singleton {
    private manifestUrl: cc.Asset = null;
    private _updating: boolean = false;
    private _canRetry: boolean = false;
    private _storagePath: string = '';

    private _am: jsb.AssetsManager = null;
    private _failCount: number = 0;
    private _checkServerVersion: Function = null;

    private __version: string = '';

    getVersion(): string {
        if(this.__version == '') return "1.0.0";
        return this.__version;
    }

    setVersion(version: string) {
        this.__version = version;
    }

    public loadManifest(name: string, callBack: Function): void {
        if (!cc.sys.isNative) {
            callBack&&callBack('');
            return;
        }
        this._checkServerVersion = callBack;
        ResManager.ins().loadManifest(name, (asset: cc.Asset) => {
            this.manifestUrl = asset;
            this.init();
        })
    }

    init(): void {
        if (!cc.sys.isNative) {
            return;
        }
        this._storagePath = ((jsb.fileUtils ? jsb.fileUtils.getWritablePath() : '/') + 'game-remote-asset');
        LogUtil.log('_storagePath>>>',this._storagePath);
        var versionCompareHandle = function (versionA, versionB) {
            var vA = versionA.split('.');
            var vB = versionB.split('.');
            for (var i = 0; i < vA.length; ++i) {
                var a = parseInt(vA[i]);
                var b = parseInt(vB[i] || 0);
                if (a === b) {
                    continue;
                } else {
                    return a - b;
                }
            }
            if (vB.length > vA.length) {
                return -1;
            }
            else {
                return 0;
            }
        };

        // LogUtil.log('manifestUrl>>',JSON.stringify(this.manifestUrl));
        LogUtil.log('nativeUrl>>', this.manifestUrl.nativeUrl)
        this._am = new jsb.AssetsManager(this.manifestUrl.nativeUrl, this._storagePath, versionCompareHandle);
        this._am.setVerifyCallback(function (filePath, asset) {
            //增加了md5校验，如果不需要可以直接return true
            let data = jsb.fileUtils.getDataFromFile(filePath);
            let fileMd5 = MD5(data);
            let ret = fileMd5 == asset.md5;
            if (!ret) {
                LogUtil.log('md5 is wrong, file:' + filePath);
            }
            return ret;
        });

        if (this._checkServerVersion) {
            this._checkServerVersion(this._am.getLocalManifest().getVersion());
        }

        //初始化脚本版本信息
        if (cc.sys.os === cc.sys.OS_ANDROID) {
            //一些安卓设备不支持同时下载文件过多
            this._am.setMaxConcurrentTask(2);
        } else {
            this._am.setMaxConcurrentTask(2);
        }
    }

    clear() {
        if (!cc.sys.isNative) {
            return;
        }
        this._am.setEventCallback(null);
        this._am = null;
    }

    showLog(msg: string) {
        LogUtil.log('[HotUpdateModule][showLog]----' + msg);
    }

    retry() {
        if (!this._updating && this._canRetry) {
            this._canRetry = false;
            this._am.downloadFailedAssets();
        }
    }

    updateCallback(event) {
        var needRestart = false;
        var failed = false;
        switch (event.getEventCode()) {
            case jsb.EventAssetsManager.ERROR_NO_LOCAL_MANIFEST:
                this.showLog("没有发现本地manifest文件，跳过了热更新.");
                failed = true;
                break;
            //更新进度
            case jsb.EventAssetsManager.UPDATE_PROGRESSION:
                let percent = event.getPercent();
                if (isNaN(percent)) return;
                var msg = event.getMessage();
                this.disPatchRateEvent(percent);
                this.showLog("updateCallback更新进度：" + percent + ', msg: ' + msg);
                break;

            //下载manifest文件失败，跳过热更新
            case jsb.EventAssetsManager.ERROR_DOWNLOAD_MANIFEST:
            case jsb.EventAssetsManager.ERROR_PARSE_MANIFEST:
                this.showLog("下载manifest文件失败，跳过热更新.");
                failed = true;
                break;

            //已是最新版本
            case jsb.EventAssetsManager.ALREADY_UP_TO_DATE:
                this.showLog("已是最新版本.");
                failed = true;
                break;
            //更新结束
            case jsb.EventAssetsManager.UPDATE_FINISHED:
                this.showLog("更新结束." + event.getMessage());
                this.disPatchRateEvent(1);
                needRestart = true;
                break;
            //更新错误
            case jsb.EventAssetsManager.UPDATE_FAILED:
                this.showLog("更新错误." + event.getMessage());
                this._updating = false;
                this._canRetry = true;
                this._failCount++;
                this.retry();
                break;
            //更新过程中错误
            case jsb.EventAssetsManager.ERROR_UPDATING:
                this.showLog('更新过程中错误: ' + event.getAssetId() + ', ' + event.getMessage());
                break;
            //解压错误
            case jsb.EventAssetsManager.ERROR_DECOMPRESS:
                this.showLog('解压错误');
                break;
            default:
                break;
        }

        if (failed) {
            this._am.setEventCallback(null);
            this._updating = false;
        }

        if (needRestart) {
            this._am.setEventCallback(null);
            var searchPaths = jsb.fileUtils.getSearchPaths();
            var newPaths = this._am.getLocalManifest().getSearchPaths();
            Array.prototype.unshift.apply(searchPaths, newPaths);
            cc.sys.localStorage.setItem('HotUpdateSearchPaths', JSON.stringify(searchPaths));
            jsb.fileUtils.setSearchPaths(searchPaths);
            cc.audioEngine.stopAll();
            setTimeout(() => {
                cc.game.restart();
            }, 100);
        }
    }

    hotUpdate() {
        if (!cc.sys.isNative) {
            return;
        }
        LogUtil.log('this._updating>>>>>>', this._updating);
        if (this._am && !this._updating) {
            this._am.setEventCallback(this.updateCallback.bind(this));
            if (this._am.getState() === jsb.AssetsManager.State.UNINITED) {
                var url = this.manifestUrl.nativeUrl;
                this._am.loadLocalManifest(url);
            }
            this._failCount = 0;
            this._am.update();
            this._updating = true;
        }
    }

    //检测更新状态
    checkCallback(event) {
        switch (event.getEventCode()) {
            case jsb.EventAssetsManager.ERROR_NO_LOCAL_MANIFEST:
                this.showLog("没有发现本地manifest文件，跳过了热更新.");
                this.hotUpdateFinish(true);
                break;
            case jsb.EventAssetsManager.ERROR_DOWNLOAD_MANIFEST:
            case jsb.EventAssetsManager.ERROR_PARSE_MANIFEST:
                this.showLog("下载manifest文件失败，跳过热更新.");
                this.hotUpdateFinish(false);
                break;
            case jsb.EventAssetsManager.ALREADY_UP_TO_DATE:
                this.showLog("已更新.");
                this.hotUpdateFinish(true);
                break;
            case jsb.EventAssetsManager.NEW_VERSION_FOUND: {
                //有新版本
                let _total = event.getTotalBytes();
                // let _have = event.getDownloadedBytes();
                let total: string;
                if (_total < 1048576) {                              // 小于1m，就显示kb
                    _total = Math.ceil(_total / 1024)
                    total = _total + 'K'
                }else {                                               // 显示m
                    total = (_total / (1024 * 1024)).toFixed(1);
                    total = total + 'M'
                }
                this.showLog("有新版本,需要更新,大小"+total);
                PopupManager.show('prefabs/PopBox', {
                    title: '提示',
                    content: "有新版本,需要更新,大小"+total,
                    confirmCallback: () => {
                        this._updating = false;
                        this.hotUpdate();
                    },
                    cancelCallback: () => {
                        this._am.setEventCallback(null);
                        this._updating = false;
                        EventManager.emit(CommonEvents.HOTUPDATE_CANCEL);
                    }
                });
                // this._updating = false;
                // this.hotUpdate();
                return;
            }
            case jsb.EventAssetsManager.UPDATE_PROGRESSION: {
                //有新版本
                let percent = event.getPercent();
                if (isNaN(percent)) return;
                var msg = event.getMessage();
                this.showLog("checkCallback更新进度：" + percent + ', msg: ' + msg);
                return;
            }
            default:
                console.log('event.getEventCode():' + event.getEventCode());
                return;
        }
        this._am.setEventCallback(null);
        this._updating = false;
    }

    checkUpdate() {
        if (this._updating) {
            LogUtil.log("检测更新中...");
            return;
        }
        if (this._am.getState() === jsb.AssetsManager.State.UNINITED) {
            LogUtil.log("_am.getState...",this._am.getState());
            var url = this.manifestUrl.nativeUrl;
            if (cc.assetManager.md5Pipe) {
                url = cc.assetManager.md5Pipe.transformURL(url);
            }
            this._am.loadLocalManifest(url);
        }
        if (!this._am.getLocalManifest() || !this._am.getLocalManifest().isLoaded()) {
            this.showLog('加载manifest文件失败');
            return;
        }
        this._am.setEventCallback(this.checkCallback.bind(this));
        this._am.checkUpdate();
        this._updating = true;
        this.disPatchRateEvent(0.01);
    }

    //热更完成
    hotUpdateFinish(result) {
        EventManager.emit(CommonEvents.HOTUPDATE_FINISH, result);
    }

    disPatchRateEvent(percent) {
        if (percent > 1) {
            percent = 1;
        }
        EventManager.emit(CommonEvents.HOTUPDATE_RATE, percent);
    }
}